package com.iflyings.android.dataconfig

import android.database.Cursor
import android.database.MatrixCursor
import java.io.File
import java.io.IOException
import java.util.regex.Pattern

internal class IniSection private constructor(private val mIniFile: File) {

    data class Section(var name: String, val values: MutableMap<String, String> = LinkedHashMap()) {
        operator fun set(key: String, value: String) {
            values[key] = value
        }
        operator fun get(key: String): String? {
            return values[key]
        }
        fun remove(key: String) {
            values.remove(key)
        }

        fun isEmpty() = values.isEmpty()
    }

    /**
     * 换行符
     */
    private val mLineSeparator = System.lineSeparator()
    private val mSectionMaps: MutableMap<String, Section> = LinkedHashMap()

    /**
     * 设置值
     *
     * @param section 节点
     * @param key     属性名
     * @param value   属性值
     */
    operator fun set(section: String, key: String, value: String) {
        val s = mSectionMaps[section] ?:
                    Section(section).apply { mSectionMaps[section] = this }
        if (s[key] != value) {
            s[key] = value
            saveData()
        }
    }

    /**
     * 获取节点
     *
     * @param section 节点名称
     * @return
     */
    operator fun get(section: String, key: String): String? {
        return mSectionMaps[section]?.get(key)
    }

    /**
     * 删除节点
     *
     * @param section 节点名称
     */
    fun remove(section: String) = mSectionMaps.remove(section)

    /**
     * 删除属性
     *
     * @param section 节点名称
     * @param key     属性名称
     */
    fun remove(section: String, key: String) {
        val s = mSectionMaps[section] ?: return
        s.remove(key)
        if (s.isEmpty()) {
            mSectionMaps.remove(section)
        }
    }

    @Throws(IOException::class)
    private fun loadData() {
        FileUtils.readText(mIniFile.absolutePath) { reader ->
            val pattern = Pattern.compile("^\\[.*]$")
            var currSection: Section? = null

            while (true) {
                var line = reader.readLine() ?: break
                if (pattern.matcher(line).matches()) {
                    line = line.trim { it <= ' ' }
                    val name = line.substring(1, line.length - 1)
                    currSection = Section(name)
                    mSectionMaps[name] = currSection
                } else if (line.isNotBlank()) {
                    val idx = line.indexOf("=")
                    if (idx > 0) {
                        val key = line.substring(0, idx)
                        val value = line.substring(idx + 1)
                        currSection!![key] = value
                    } else {
                        mSectionMaps.clear()
                        return@readText false
                    }
                }
            }
            return@readText true
        }
    }

    @Throws(IOException::class)
    private fun saveData() {
        FileUtils.writeText(mIniFile.absolutePath) {
            for (section in mSectionMaps.values) {
                it.append("[${section.name}]").append(mLineSeparator)
                for ((key, value) in section.values) {
                    it.append("$key=$value").append(mLineSeparator)
                }
            }
        }
    }

    class IniCursor(initialCapacity: Int): MatrixCursor(arrayOf(SECTION, NAME, VALUE), initialCapacity) {

        constructor(): this(16)

        companion object {
            fun from(section: String, name: String, value: String): IniCursor {
                return IniCursor(1).apply {
                    addRow(arrayOf(section, name, value))
                }
            }
        }
    }

    fun getAllData(): Cursor {
        val cursor = IniCursor()
        mSectionMaps.forEach { (_, section) ->
            section.values.forEach { (name, value) ->
                cursor.addRow(arrayOf(section.name, name, value))
            }
        }
        return cursor
    }

    fun getAllData(section: String): Cursor {
        val cursor = IniCursor()
        mSectionMaps[section]?.values?.forEach { (name, value) ->
            cursor.addRow(arrayOf(section, name, value))
        }
        return cursor
    }

    fun getItemData(section: String, name: String): Cursor {
        val cursor = IniCursor(1)
        val value = get(section, name)
        if (value != null) {
            cursor.addRow(arrayOf(section, name, value))
        }
        return cursor
    }

    companion object {
        const val SECTION = "section"
        const val NAME = "name"
        const val VALUE = "value"
        private var mInstance: IniSection? = null

        @Throws(IOException::class)
        fun init(filePath: String) {
            if (mInstance == null) {
                synchronized(IniSection::class) {
                    if (mInstance == null) {
                        val configFile = File(filePath)
                        FileUtils.mkdirs(configFile.parentFile!!)
                        mInstance = IniSection(configFile)
                        mInstance!!.loadData()
                    }
                }
            }
        }

        fun get() = mInstance!!

        fun getValue(section: String, name: String): String? {
            return get()[section, name]
        }

        fun setValue(section: String, name: String, value: String) {
            get()[section, name] = value
        }

        fun remove(section: String) = get().remove(section)

        fun remove(section: String, name: String) = get().remove(section, name)
    }
}